技巧80 创建另一个Docker虚拟网络

在人们首次了解创建自有虚拟网络的能力时,一个常见的反应是询问如何创建默认Docker网桥的副本,以便一组容器进行通信,但又是与其他容器隔离。

Docker公司意识到这将是一个大众化要求,因此在最初的实验性版本就将其作为虚拟网络首批功能之一实现了。

问题

想要一个Docker公司支持的用于创建虚拟网络的解决方案。

解决方案

使用嵌套在 docker network 下面的Docker子命令集来创建自己的虚拟网络。

内建的“网桥”驱动可能是最常用的驱动,它受到官方支持,允许你创建默认的内建网桥的新副本。但在本技巧的后面我们会看到两者的一个重要区别:在非默认的网桥里,你可以通过名称来ping容器。

使用 docker network ls 命令可以看到内建的网络列表:

$ docker network ls
NETWORK ID          NAME           DRIVER             SCOPE
100ce06cd9a8        bridge         bridge             local
d53919a3bfa1        host           host               local
2d7fcd86306c        none           null               local

在此可以看到,我的机器上有3个网络可供容器加入。 bridge 网络是容器默认具有的,使其可与网桥上的其他容器对话。 host 网络指定了在启动容器时使用 --net=host 会发生什么(容器可以像机器上的其他正常程序那样看到这个网络),而 none 对应的是 --net=none ,容器将只有回送网卡。

我们来增加一个新的 bridge 网络,为容器提供一个可自由通信的新的扁平网络:

$ docker network create --driver=bridge mynet
770ffbc81166d54811ecf9839331ab10c586329e72cea2eb53a0229e53e8a37f
$ docker network ls | grep mynet
770ffbc81166        mynet               bridge              local
$ ip addr | grep br-
522: br-91b29e0d29d5: <NO-
     CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group
➥ default
     inet 172.18.0.1/16 scope global br-91b29e0d29d5
$ ip addr | grep docker
5: docker0: <NO-
     CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group
➥ default
     inet 172.17.0.1/16 scope global docker0

这将创建一个新的网卡,使用与正常Docker网桥不同的IP地址范围。对于网桥,目前新的网卡名称将以 br- 开头,不过这点未来可能会有变化。

现在我们来启动两个附加到该网络的容器:

$ docker run -it -d --name c1 ubuntu:14.04.2 bash  ⇽--- 启动一个名为c1的容器(在默认网桥上)
 87c67f4fb376f559976e4a975e3661148d622ae635fae4695747170c00513165
$ docker network connect mynet c1  ⇽--- 使用mynet网络连接c1容器
$ docker run -it -d --name c2 \
--net=mynet ubuntu:14.04.2 bash  ⇽--- 在mynet网络里创建一个名为c2的容器
 0ee74a3e3444f27df9c2aa973a156f2827bcdd0852c6fd4ecfd5b152846dea5b
$ docker run -it -d --name c3 ubuntu:14.04.2 bash  ⇽--- 启动一个名为c3的容器(在默认网桥上)

前面的命令演示了将容器连接到网络两种不同的方法——启动容器然后附加到服务上,以及在一个步骤里创建并附加。

两者之间有一个差异。前者会在启动时加入默认网络(通常是Docker网桥,不过这个可以通过Docker守护进程的参数自定义),然后添加一个新的网卡以便同时访问 mynet 。后者将只会加入 mynet ——普通Docker网桥上的任何容器都无法访问它。

我们来做些连通性检查。首先,看一下容器的IP地址:

$ docker exec c1 ip addr | grep 'inet.*eth'  ⇽--- 列出c1的网卡及IP地址—— 一个在默认网桥上,一个在mynet上
    inet 172.17.0.2/16 scope global eth0
    inet 172.18.0.2/16 scope global eth1
$ docker exec c2 ip addr | grep 'inet.*eth'  ⇽--- 列出c2的网卡和IP地址——在mynet内部
   inet 172.18.0.3/16 scope global eth0
$ docker exec c3 ip addr | grep 'inet.*eth'
    inet 172.17.0.3/16 scope global eth0  ⇽--- 列出c3的网卡及IP地址—— 在默认网桥上

现在我们可以做一些连通性测试:

$ docker exec c2 ping -qc1 c1  ⇽--- 尝试从容器2 ping容器1的名称(成功)
 PING c1 (172.18.0.2) 56(84) bytes of data.
--- c1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.041/0.041/0.041/0.000 ms
$ docker exec c2 ping -qc1 c3  ⇽--- 
 ping: unknown host c3
$ docker exec c2 ping -qc1 172.17.0.3  ⇽--- 尝试从容器2 ping容器3的名称和IP地址(失败)
 PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
--- 172.17.0.3 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
$ docker exec c1 ping -qc1 c2  ⇽--- 尝试从容器1 ping容器2的名称(成功)
 PING c2 (172.18.0.3) 56(84) bytes of data.
--- c2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.047/0.047/0.047/0.000 ms
$ docker exec c1 ping -qc1 c3  ⇽--- 
 ping: unknown host c3
$ docker exec c1 ping -qc1 172.17.0.3  ⇽--- 尝试从容器1 ping容器3的名称和IP地址(失败,成功)
 PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
--- 172.17.0.3 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.095/0.095/0.095/0.000 ms

这里东西很多!以下是主要的收获。

  • 在新的网桥上,容器可以通过IP地址和名称ping通彼此。
  • 在默认网桥上,容器只能通过IP地址ping通彼此。
  • 跨多网桥的容器可以访问它们所属的任意网络上的容器。
  • 容器无法跨网桥相互访问,即便使用IP地址也不行。

讨论

这个新的网桥创建功能曾与技巧77中的Docker Compose及技巧79中的Blockade一同使用来为容器提供通过名称相互ping的能力。不过你也看到了,这是一个高度灵活的功能,具备建立相当复杂的网络模型的潜力。

举个例子,你可能想在一台 堡垒机 (提供对另一个更高价值网络的访问的一台锁定机器)做实验。通过将应用程序服务放在一个新的网桥中,然后仅通过同时连接默认网桥和新网桥的容器公开这些服务,你就可以在自己的机器上开始运行一些比较真实的渗透测试,同时保持隔离。

results matching ""

    No results matching ""